﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using SlimDX.D3DCompiler;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using System.Runtime.InteropServices;
using Apparat.ShaderManagement;

namespace Apparat.Renderables
{
  public class Box : Renderable
  {
    SlimDX.Direct3D11.Buffer vertexBuffer;
    SlimDX.Direct3D11.Buffer indexBuffer;
    DataStream vertices;
    DataStream indices;

    int vertexStride = 0;
    int numVertices = 0;
    int indexStride = 0;
    int numIndices = 0;

    int vertexBufferSizeInBytes = 0;
    int indexBufferSizeInBytes = 0;

    public Box(float width, float height, float depth)
    {
      // half length of an edge
      float offsetWidth = width / 2.0f;
      float offsetHeight = height / 2.0f;
      float offsetDepth = depth / 2.0f;

      vertexStride = Marshal.SizeOf(typeof(Vector3)); // 12 bytes
      numVertices = 8;
      vertexBufferSizeInBytes = vertexStride * numVertices;

      vertices = new DataStream(vertexBufferSizeInBytes, true, true);

      vertices.Write(new Vector3(+offsetWidth, +offsetHeight, +offsetDepth)); // 0
      vertices.Write(new Vector3(+offsetWidth, +offsetHeight, -offsetDepth)); // 1
      vertices.Write(new Vector3(-offsetWidth, +offsetHeight, -offsetDepth)); // 2
      vertices.Write(new Vector3(-offsetWidth, +offsetHeight, +offsetDepth)); // 3

      vertices.Write(new Vector3(-offsetWidth, -offsetHeight, +offsetDepth)); // 4
      vertices.Write(new Vector3(+offsetWidth, -offsetHeight, +offsetDepth)); // 5
      vertices.Write(new Vector3(+offsetWidth, -offsetHeight, -offsetDepth)); // 6
      vertices.Write(new Vector3(-offsetWidth, -offsetHeight, -offsetDepth)); // 7

      vertices.Position = 0;

      vertexBuffer = new SlimDX.Direct3D11.Buffer(
         DeviceManager.Instance.device,
         vertices,
         vertexBufferSizeInBytes,
         ResourceUsage.Default,
         BindFlags.VertexBuffer,
         CpuAccessFlags.None,
         ResourceOptionFlags.None,
         0);

      numIndices = 36;
      indexStride = Marshal.SizeOf(typeof(short)); // 2 bytes
      indexBufferSizeInBytes = numIndices * indexStride;

      indices = new DataStream(indexBufferSizeInBytes, true, true);

      // Cube has 6 sides: top, bottom, left, right, front, back

      // top
      indices.WriteRange(new short[] { 0, 1, 2 });
      indices.WriteRange(new short[] { 2, 3, 0 });

      // right
      indices.WriteRange(new short[] { 0, 5, 6 });
      indices.WriteRange(new short[] { 6, 1, 0 });

      // left
      indices.WriteRange(new short[] { 2, 7, 4 });
      indices.WriteRange(new short[] { 4, 3, 2 });

      // front
      indices.WriteRange(new short[] { 1, 6, 7 });
      indices.WriteRange(new short[] { 7, 2, 1 });

      // back
      indices.WriteRange(new short[] { 3, 4, 5 });
      indices.WriteRange(new short[] { 5, 0, 3 });

      // bottom
      indices.WriteRange(new short[] { 6, 5, 4 });
      indices.WriteRange(new short[] { 4, 7, 6 });

      indices.Position = 0;

      indexBuffer = new SlimDX.Direct3D11.Buffer(
          DeviceManager.Instance.device,
          indices,
          indexBufferSizeInBytes,
          ResourceUsage.Default,
          BindFlags.IndexBuffer,
          CpuAccessFlags.None,
          ResourceOptionFlags.None,
          0);

    }

    EffectWrapperTransformEffectWireframe ew = ShaderManager.Instance.transformEffectWireFrame;

    public override void Render()
    {
      Matrix ViewPerspective = CameraManager.Instance.ViewPerspective;
      Matrix WorldViewPerspective = this.transform * ViewPerspective;
      ew.tmat.SetMatrix(WorldViewPerspective);

      Vector4 col = new Vector4(0, 0, 0, 1);
      ew.mCol.Set(new Color4(1, 0, 1, 0));
      ew.wfCol.Set(col);

      DeviceManager.Instance.context.InputAssembler.InputLayout = ew.layout;
      DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
      DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, vertexStride, 0));
      DeviceManager.Instance.context.InputAssembler.SetIndexBuffer(indexBuffer, Format.R16_UInt, 0);

      ew.technique = ew.effect.GetTechniqueByName("Render");

      EffectTechniqueDescription techDesc;
      techDesc = ew.technique.Description;

      for (int p = 0; p < techDesc.PassCount; ++p)
      {
        ew.technique.GetPassByIndex(p).Apply(DeviceManager.Instance.context);
        DeviceManager.Instance.context.DrawIndexed(numIndices, 0, 0);
      }
    }

    public override void Dispose()
    {
      vertexBuffer.Dispose();
    }

    public override Matrix Transform
    {
      get
      {
        return this.transform;
      }
      set
      {
        this.transform = value;
      }
    }
  }
}
